package JET.physics;

import JET.Centers.ControlCenter;
import JET.Constants;
import JET.geom.Rect2d;
import JET.geom.Vec2d;
import JET.physics.bodies.RigidBody;
import JET.physics.interfaces.SceneTreeNode;

/**
 *
 * @author 057Ry
 */
public final class Spring implements Constants, SceneTreeNode
{
    public enum Type {
        Engine,
        SpringBody,
        SceneItem,
        ShapeMatch
    }

    public enum BodyType {
        Spring,
        Rigid
    }

    static int id=0;
    int sid;
    
    private Mass m1,m2;
//    private BodyType    m1Type = BodyType.Spring,
//                        m2Type = BodyType.Spring;

//    public final Vec2d m1RigidPos = new Vec2d();
//    public final Vec2d m2RigidPos = new Vec2d();
    //public RigidBody rb1, rb2;

    private double startLength; // dlugosc poczatkowa
    private double ks;          // stala sprezystosci
    private double kd;          // stala tlumiaca drgania
    private double kt;          // stara rozdarcia (wzgledem dlugosci poczatkowej)
    private boolean tearable    = false;
    private boolean alive       = true;
    public static double initKt = 3.5;
    private Type type = Type.Engine;

    public String name = "";

    public Spring(Type t) {
        this.ks = 10.0;
        this.kd = 0.5;
        this.kt = initKt;
        type = t;
        
//        setM1Type(BodyType.Spring);
//        setM2Type(BodyType.Spring);
    }
    
    public Spring(Mass m1, Mass m2, Type t)
    {
        sid = id++;
        name = "Spring_"+sid;

        this.m1 = m1;
        this.m2 = m2;
        this.startLength = getVector().getMagnitude();
        this.ks = 10.0;
        this.kd = 0.5;

        this.kt = initKt;
        this.type = t;

//        setM1Type(BodyType.Spring);
//        setM2Type(BodyType.Spring);
    }
    public Spring(Mass m1, Mass m2, Type t, double sLength)
    {
        sid = id++;
        name = "Spring_"+sid;

        this.m1 = m1;
        this.m2 = m2;
        this.startLength = sLength;
        this.ks = 10.0;
        this.kd = 0.5;

        this.kt = initKt;
        this.type = t;

//        setM1Type(BodyType.Spring);
//        setM2Type(BodyType.Spring);
    }

//    public BodyType getM1Type() { return m1Type; }
//    public void setM1Type(BodyType t) { m1Type = t; }
//    public BodyType getM2Type() { return m2Type; }
//    public void setM2Type(BodyType t) { m2Type = t; }

    public Type getType() {
        return type;
    }
    public void setType(Type t) {
        type = t;
    }

    public String getName() {
        return name;
    }
    public void setName(String n) {
        name = n;
    }

    public void setM1(Mass m1) {
        this.m1 = m1;
    }

    public void setM2(Mass m2) {
        this.m2 = m2;
    }
    
    public Mass getM1()
    {
        return m1;
    }
    public Mass getM2()
    {
        return m2;
    }

    public void setKs(double ks)
    {
        this.ks = ks;
    }
    public void setKd(double kd)
    {
        this.kd = kd;
    }
    public void setStartLength(double sl)
    {
        this.startLength = sl;
    }
    public void setTearable(boolean t) {
        this.tearable = t;
    }
    public void setKt(double kt) {
        this.kt = kt;
    }
    public void setAlive(boolean a) {
        this.alive = a;
    }

    public double getKd() {
        return kd;
    }

    public double getKs() {
        return ks;
    }

    public double getStartLength() {
        return startLength;
    }
    public boolean isTearable() {
        return tearable;
    }
    public double getKt() {
        return kt;
    }
    public boolean isAlive() {
        return alive;
    }
    public double getVx()
    {
        return m2.getPosition().getX() - m1.getPosition().getX();
    }
    public double getVy()
    {
        return m2.getPosition().getY() - m1.getPosition().getY();
    }
    public Vec2d getVector()
    {
        return new Vec2d( getVx(), getVy() );
    }
    
    // Najważniejsza metoda klasy Spring.
    // Odpowiada za zastosowanie wzorów do obliczenia sił jakimi sprężyna działa na
    // obiekty implementujące interface Mass (Mass - masy punktowe).
    public void doYourJob()
    {
        if (alive) {

//            if (m1Type == BodyType.Rigid) {
//                m1.getPosition().become(m1RigidPos);
//                rb1.updateMassData(m1);
//                rb1.baseToScene(m1.getPosition());
//            }
//            if (m2Type == BodyType.Rigid) {
//                m2.getPosition().become(m2RigidPos);
//                rb2.updateMassData(m2);
//                rb2.baseToScene(m2.getPosition());
//            }

            Vec2d r12, m12, Fw;
            double fs, fd;

            r12 = new Vec2d( m1.getPosition() );
            r12.sub( m2.getPosition() );
            double r12Magnitude = r12.getMagnitude();

            if (tearable && r12Magnitude>startLength*kt) {
                alive= false;
                return;
            }
            
            if (r12Magnitude >= 0.001) {
                m12= new Vec2d( m1.getVelocity() );
                m12.sub( m2.getVelocity() );

                fs = ks * (r12Magnitude - startLength);
                r12.normalize();
                fd = kd * m12.dot( r12 );

                Fw = new Vec2d( r12 );
                Fw.scale( fs+fd );

                m1.getForce().sub( Fw );
                m2.getForce().add( Fw );
//                switch(m1Type) {
//                    case Spring : m1.getForce().sub( Fw ); break;
//                    case Rigid  : rb1.applyForce(Fw.getScaled(-1), m1.getPosition()); break;
//                }
//
//                switch(m2Type) {
//                    case Spring : m2.getForce().add( Fw ); break;
//                    case Rigid  : rb2.applyForce(Fw, m2.getPosition()); break;
//                }
            }
        }
    }
    
    public Rect2d getCatchArea(double catchAreaScale)
    {
        double x = this.m1.getPosition().getX()+(this.m2.getPosition().getX() - this.m1.getPosition().getX())/2;
        double y = this.m1.getPosition().getY()+(this.m2.getPosition().getY() - this.m1.getPosition().getY())/2;
        
        return new Rect2d( x - SPRING2D_CATCH_AREA_SIZE/catchAreaScale, y - SPRING2D_CATCH_AREA_SIZE/catchAreaScale,
                           x + SPRING2D_CATCH_AREA_SIZE/catchAreaScale, y + SPRING2D_CATCH_AREA_SIZE/catchAreaScale );
    }
    public Vec2d getCenter()
    {
        double x = this.m1.getPosition().getX() + (this.m2.getPosition().getX() - this.m1.getPosition().getX())/2;
        double y = this.m1.getPosition().getY() + (this.m2.getPosition().getY() - this.m1.getPosition().getY())/2;

        return new Vec2d(x, y);
    }

    public String toString() {
        return name;
    }

    ////////////////////////////////////////////////////////////////////////////
    //                S C E N E   T R E E   I N T E R F A C E                 //
    ////////////////////////////////////////////////////////////////////////////

    public NameTreeNode nameTreeNode = new NameTreeNode();
    public KsTreeNode ksTreeNode = new KsTreeNode();
    public KdTreeNode kdTreeNode = new KdTreeNode();
    public KtTreeNode ktTreeNode = new KtTreeNode();
    public StartLengthTreeNode startLengthTreeNode = new StartLengthTreeNode();
    public TearableTreeNode tearableTreeNode = new TearableTreeNode();
    public AliveTreeNode aliveTreeNode = new AliveTreeNode();

    public SceneTreeNode getChild(int index) {
        switch (index) {
            case 0 : return nameTreeNode;
            case 1 : return ksTreeNode;
            case 2 : return kdTreeNode;
            case 3 : return ktTreeNode;
            case 4 : return startLengthTreeNode;
            case 5 : return tearableTreeNode;
            case 6 : return aliveTreeNode;
            case 7 : return m1;
            case 8 : return m2;
        }

        return null;
    }

    public int getChildCount() {
        return 9;
    }

    public boolean isLeaf() {
        return false;
    }

    public int getIndexOfChild(Object child) {
        if      (child instanceof NameTreeNode) return 0;
        if      (child instanceof KsTreeNode) return 1;
        if      (child instanceof KdTreeNode) return 2;
        if      (child instanceof KtTreeNode) return 3;
        if      (child instanceof StartLengthTreeNode) return 4;
        if      (child instanceof TearableTreeNode) return 5;
        if      (child instanceof AliveTreeNode) return 6;
        else if  (child instanceof Mass && child.equals(m1)) return 7;
        else if  (child instanceof Mass && child.equals(m2)) return 8;
        else
            return -1;
    }

    public void resetNodeValue(Object o) {}

    // Nodes Internal Classes

    public class NameTreeNode implements SceneTreeNode {
        public SceneTreeNode getChild(int index) {return null;}
        public int getChildCount() {return 0;}
        public boolean isLeaf() {return true;}
        public int getIndexOfChild(Object child) {return -1;}

        public void resetNodeValue(Object o) {
            name = o.toString();
        }

        public String toString() {
            return "name: '"+name+"'";
        }
    }

    public class KsTreeNode implements SceneTreeNode {
        public SceneTreeNode getChild(int index) {return null;}
        public int getChildCount() {return 0;}
        public boolean isLeaf() {return true;}
        public int getIndexOfChild(Object child) {return -1;}

        public void resetNodeValue(Object o) {
            try {
                ks = Double.parseDouble(o.toString());
                ControlCenter.getInstance().GUIC.repaintAllSceneTabs();
            } catch(NumberFormatException nfe) {}
        }

        public String toString() {
            return "ks: '"+ks+"'";
        }
    }

    public class KdTreeNode implements SceneTreeNode {
        public SceneTreeNode getChild(int index) {return null;}
        public int getChildCount() {return 0;}
        public boolean isLeaf() {return true;}
        public int getIndexOfChild(Object child) {return -1;}

        public void resetNodeValue(Object o) {
            try {
                kd = Double.parseDouble(o.toString());
                ControlCenter.getInstance().GUIC.repaintAllSceneTabs();
            } catch(NumberFormatException nfe) {}
        }

        public String toString() {
            return "kd: '"+kd+"'";
        }
    }

    public class KtTreeNode implements SceneTreeNode {
        public SceneTreeNode getChild(int index) {return null;}
        public int getChildCount() {return 0;}
        public boolean isLeaf() {return true;}
        public int getIndexOfChild(Object child) {return -1;}

        public void resetNodeValue(Object o) {
            try {
                kt = Double.parseDouble(o.toString());
                ControlCenter.getInstance().GUIC.repaintAllSceneTabs();
            } catch(NumberFormatException nfe) {}
        }

        public String toString() {
            return "kt: '"+kt+"'";
        }
    }

    public class StartLengthTreeNode implements SceneTreeNode {
        public SceneTreeNode getChild(int index) {return null;}
        public int getChildCount() {return 0;}
        public boolean isLeaf() {return true;}
        public int getIndexOfChild(Object child) {return -1;}

        public void resetNodeValue(Object o) {
            try {
                startLength = Double.parseDouble(o.toString());
                ControlCenter.getInstance().GUIC.repaintAllSceneTabs();
            } catch(NumberFormatException nfe) {}
        }

        public String toString() {
            return "start length: '"+startLength+"'";
        }
    }

    public class TearableTreeNode implements SceneTreeNode {
        public SceneTreeNode getChild(int index) {return null;}
        public int getChildCount() {return 0;}
        public boolean isLeaf() {return true;}
        public int getIndexOfChild(Object child) {return -1;}

        public void resetNodeValue(Object o) {
            tearable = Boolean.parseBoolean(o.toString());
            ControlCenter.getInstance().GUIC.repaintAllSceneTabs();
        }

        public String toString() {
            return "tearable: "+tearable;
        }
    }

    public class AliveTreeNode implements SceneTreeNode {
        public SceneTreeNode getChild(int index) {return null;}
        public int getChildCount() {return 0;}
        public boolean isLeaf() {return true;}
        public int getIndexOfChild(Object child) {return -1;}

        public void resetNodeValue(Object o) {
            alive = Boolean.parseBoolean(o.toString());
            ControlCenter.getInstance().GUIC.repaintAllSceneTabs();
        }

        public String toString() {
            return "alive: "+alive;
        }
    }
}